/*! ******************************************************************************
 *
 * Pentaho
 *
 * Copyright (C) 2024 by Hitachi Vantara, LLC : http://www.pentaho.com
 *
 * Use of this software is governed by the Business Source License included
 * in the LICENSE.TXT file.
 *
 * Change Date: 2029-07-20
 ******************************************************************************/

package com.pentaho.repository.importexport;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.xml.XMLParserFactoryProducer;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.repository.utils.IRepositoryFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class PDIImportUtil {

  private static IRepositoryFactory repositoryFactory = new IRepositoryFactory.CachingRepositoryFactory();
  private static final LogChannelInterface log = new LogChannel( PDIImportUtil.class );

  /**
   * Connects to the PDI repository
   *
   * @param repositoryName
   * @return
   * @throws KettleException
   */
  public static Repository connectToRepository( String repositoryName ) throws KettleException {
    return repositoryFactory.connect( repositoryName );
  }

  public static void setRepositoryFactory( IRepositoryFactory factory ) {
    repositoryFactory = factory;
  }

  public static Document loadXMLFrom( String xml ) throws SAXException, IOException {
    return loadXMLFrom( new ByteArrayInputStream( xml.getBytes() ) );
  }

  /**
   * @return instance of {@link Document}, if xml is loaded successfully null in case any error occurred during loading
   */
  public static Document loadXMLFrom( InputStream is ) {
    DocumentBuilderFactory factory = getSecureDocumentBuilderFactory();
    Document doc = null;
    if (factory != null) {
      try {
        File file = File.createTempFile("tempFile", "temp");
        file.deleteOnExit();
        FileOutputStream fous = new FileOutputStream(file);
        IOUtils.copy(is, fous);
        fous.flush();
        fous.close();
        DocumentBuilder builder = factory.newDocumentBuilder();
        doc = builder.parse(file);
      } catch (IOException | SAXException | ParserConfigurationException e) {
        log.logError(e.getLocalizedMessage());
      } finally {
        IOUtils.closeQuietly(is);
      }
    }
    return doc;
  }

  private static DocumentBuilderFactory getSecureDocumentBuilderFactory() {
    DocumentBuilderFactory factory;
    try {
      factory = XMLParserFactoryProducer.createSecureDocBuilderFactory();
    } catch (ParserConfigurationException e) {
      log.logError(e.getLocalizedMessage());
      factory = DocumentBuilderFactory.newInstance();
      try {
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
      } catch (ParserConfigurationException ex) {
        log.logError(ex.getMessage());
        factory = null;
      }
    }
    return factory;
  }

  public static String asXml( Document document ) {
    try {
      Source source = new DOMSource( document.getParentNode() );
      StringWriter stringWriter = new StringWriter();
      Result result = new StreamResult( stringWriter );
      TransformerFactory factory = TransformerFactory.newInstance();
      factory.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
      factory.setAttribute( XMLConstants.ACCESS_EXTERNAL_DTD, "" );
      factory.setAttribute( XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" );
      Transformer transformer = factory.newTransformer();
      transformer.transform( source, result );
      return stringWriter.getBuffer().toString();
    } catch ( TransformerException e ) {
      e.printStackTrace();
      return null;
    }
  }


}
